//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;
using System.Drawing;
using Microsoft.TridentWordAddIn.Server;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// This user control is used for displaying the associated Workflow with an image
    /// </summary>
    public partial class ViewAssociatedWorkflowsTaskPane : BaseUserControl, ITaskPaneControl, IDisposable
    {
        private bool handlingRerun;
        private bool doNotUpdateUI;
        private bool isRerunPresent;
        private bool isOrginal = true;
        private ICollection<WorkflowAssociation> workflowAssociationList;

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewAssociatedWorkflowsTaskPane"/> class.
        /// </summary>
        public ViewAssociatedWorkflowsTaskPane()
        {
            base.Initialize(true);
            InitializeComponent();

            this.workflowAssociationList = this.ControllerInstance.GetAllWorkflowAssociations(DocumentKey, this.ControllerInstance.SelectedPicture);
            this.UpdateAssociatedWorkflows();

            this.ctrDataProductOutput.OutPutType = OutputType.DataProduct;
            this.ctrWorkflowOutput.OutPutType = OutputType.Workflow;

            this.lvWorkflowAssociations.SelectedIndexChanged += new EventHandler(lvWorkflowAssociations_SelectedIndexChanged);
            this.ctrWorkflowOutput.OnSaveResultClicked += new EventHandler<SelectedFileEventArgs>(OnSaveResultClicked);
            this.ctrWorkflowOutput.OnResultDoubleClicked += new EventHandler<SelectedFileEventArgs>(OnResultDoubleClicked);
            this.ctrWorkflowOutput.OnInsertResultClicked += new EventHandler<SelectedFileEventArgs>(OnInsertResultClicked);
            this.ctrDataProductOutput.OnSaveResultClicked += new EventHandler<SelectedFileEventArgs>(OnSaveResultClicked);
            this.ctrDataProductOutput.OnResultDoubleClicked += new EventHandler<SelectedFileEventArgs>(OnResultDoubleClicked);
            this.ctrDataProductOutput.OnInsertResultClicked += new EventHandler<SelectedFileEventArgs>(OnInsertResultClicked);
            
            ControllerInstance.OnPictureSelectionChanged += new System.EventHandler<PictureSelectionChangedEventArgs>(Controller_OnPictureSelectionChanged);
            ControllerInstance.OnAssociationChanged += new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
            ControllerInstance.OnRerunCompleted += new EventHandler<RerunCompletedEventArgs>(Controller_OnRerunCompleted);
        }

        private delegate void UIControllerDelegate();

        #region Properties

        /// <summary>
        /// Gets the selected workflow association.
        /// </summary>
        /// <returns></returns>
        private WorkflowAssociation SelectedAssociation
        {
            get
            {
                if (this.lvWorkflowAssociations.SelectedItems.Count > 0)
                {
                    return this.lvWorkflowAssociations.SelectedItems[0].Tag as WorkflowAssociation;
                }

                return null;
            }
        }

        /// <summary>
        /// Gets the trident server for linked workflow.
        /// </summary>
        /// <param name="workflow">The workflow.</param>
        /// <param name="userCancelled">if set to <c>true</c> [user cancelled].</param>
        /// <returns></returns>
        private ITridentServer GetTridentServerForLinkedWorkflow(Workflow workflow, out bool userCancelled)
        {
            userCancelled = false;
            if (workflow.Linked)
            {
                if (DialogResult.Yes == ExportLinkedWorkflow.ShowMessage())
                {
                    TridentConnection connection =
                         ExportLinkedWorkflow.DownloadFromAssociatedServer ?
                         workflow.LinkedConnection.Clone() as TridentConnection :
                         this.ControllerInstance.CurrentConnection;

                    ITridentServer tridentServer = this.ControllerInstance.ServerBroker.GetVerifiedServer(connection);
                    if (tridentServer != null && tridentServer.CurrentTridentConnection != null)
                    {
                        return tridentServer;
                    }
                }
                else
                {
                    userCancelled = true;
                }
            }

            return null;
        }

        /// <summary>
        /// Exports the workflow.
        /// </summary>
        /// <param name="workflowId">The workflow id.</param>
        /// <param name="name">The name.</param>
        /// <param name="tridentServer">The trident server.</param>
        private void ExportWorkflow(Guid workflowId, string name, ITridentServer tridentServer)
        {
            using (SaveFileDialog dialog = new SaveFileDialog())
            {
                dialog.Filter = "Zip Files (*.zip)|*.zip|All files (*.*)|*.*";
                dialog.FileName = name.Replace(':', '_') + ".zip";
                DialogResult result = dialog.ShowDialog();
                if (result == DialogResult.OK && dialog.FileName != null)
                {
                    using (DownloadZipForm form = new DownloadZipForm())
                    {
                        form.SetWork(DocumentKey, dialog.FileName, workflowId, tridentServer);
                        form.ShowDialog();
                        if (!form.ExceptionMessage.IsNullOrEmpty())
                        {
                            MessageBoxHelper.ShowDefault(form.ExceptionMessage);
                        }
                    }
                }
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Clears the workflow details.
        /// </summary>
        private void ClearWorkflowDetails()
        {
            this.ctrWorkflowActivities.Refresh(null);
            this.ctrWorkflowOutput.ClearResult();
            this.ctrDataProductOutput.ClearResult();
            this.ctrParameters.ClearParameters();
        }

        /// <summary>
        /// Displays the result.
        /// </summary>
        private void UpdateAssociatedWorkflows()
        {
            WorkflowAssociation association = this.SelectedAssociation;
            this.lvWorkflowAssociations.Items.Clear();
            if (this.workflowAssociationList.Count == 0)
            {
                this.lvWorkflowAssociations.Items.Add("No workflow associations present");
                this.SetButtonStatus(false);
            }
            else
            {
                Model model = ControllerInstance.GetModelByDocumentKey(DocumentKey);
                foreach (WorkflowAssociation workflowAssociation in this.workflowAssociationList)
                {
                    if (model.Workflows.ContainsKey(workflowAssociation.WorkflowId))
                    {
                        Workflow workflow = model.Workflows[workflowAssociation.WorkflowId];
                        ListViewItem newItem = this.lvWorkflowAssociations.Items.Add(workflowAssociation.Name);
                        newItem.ImageKey = workflow.Linked ? "LinkedAssociation" : "Association";
                        newItem.SubItems.Add(workflowAssociation.Job.Name);
                        newItem.SubItems.Add(workflowAssociation.Job.DisplayId);
                        newItem.Tag = workflowAssociation;
                    }
                }

                this.SetButtonStatus(true);
            }

            this.OnUpdatedSelecteAssociation(association);
        }

        /// <summary>
        /// Called when [association workflows updated].
        /// </summary>
        /// <param name="association">The association.</param>
        private void OnUpdatedSelecteAssociation(WorkflowAssociation association)
        {
            int index = 0;
            bool selected = false;
            string associationName = association == null ? string.Empty : association.Name;

            foreach (WorkflowAssociation workflowAssociation in this.workflowAssociationList)
            {
                if (workflowAssociation.Name.Compare(associationName) || associationName.IsNullOrEmpty())
                {
                    this.lvWorkflowAssociations.Items[index].Selected = true;
                    selected = true;
                    break;
                }

                index++;
            }

            if (selected == false)
            {
                this.lvWorkflowAssociations.Items[0].Selected = true;
                selected = true;
            }

            if (!selected)
            {
                this.ClearWorkflowDetails();
            }
        }

        /// <summary>
        /// Sets the button status.
        /// </summary>
        /// <param name="isEnable">if set to <c>true</c> [is enable].</param>
        private void SetButtonStatus(bool isEnable)
        {
            this.btnDeleteWorkflowAssociation.Enabled = isEnable;
            this.btnExport.Enabled = isEnable;
            this.btnEditWorkflowAssociation.Enabled = isEnable;
            this.btnRerun.Enabled = isEnable;
        }

        /// <summary>
        /// Updates the workflow details.
        /// </summary>
        /// <param name="workflowAssociation">The workflow association.</param>
        /// <param name="associationChanged">if set to <c>true</c> [association changed].</param>
        private void UpdateWorkflowDetails(WorkflowAssociation workflowAssociation, bool associationChanged)
        {
            if (this.doNotUpdateUI)
            {
                return;
            }

            if (workflowAssociation != null)
            {
                Workflow workflow = this.ControllerInstance.WorkflowById(DocumentKey, workflowAssociation.WorkflowId);
                this.pnlOriginalToRerunSwitch.Visible = this.isRerunPresent;
                if (associationChanged)
                {
                    this.ctrWorkflowActivities.Refresh(workflow);
                    this.txtAnnotation.Text = workflowAssociation.Annotation;
                }

                WorkflowJobStatus jobStatus = WorkflowJobStatus.Finished;
                if (this.isRerunPresent && workflowAssociation.RerunJob.JobStatus != WorkflowJobStatus.Finished)
                {
                    jobStatus = this.UpdateRerunJob(workflowAssociation);
                    this.rerunTimer.Enabled = jobStatus == WorkflowJobStatus.Processing;
                }

                if (this.isRerunPresent && this.rbtnRerun.Checked)
                {
                    if (!this.handlingRerun)
                    {
                        this.ShowRerunResult(workflowAssociation.RerunJob);
                    }
                }
                else
                {
                    this.ShowOriginalJobResult(workflowAssociation.Job);
                }

                this.ShowParameters(this.isRerunPresent && this.rbtnRerun.Checked);
                return;
            }

            this.ClearWorkflowDetails();
            this.txtAnnotation.Clear();
        }

        /// <summary>
        /// Shows the parameters.
        /// </summary>
        /// <param name="isRerun">if set to <c>true</c> [is rerun].</param>
        private void ShowParameters(bool isRerun)
        {
            WorkflowAssociation workflowAssociation = this.SelectedAssociation;
            Workflow workflow = this.ControllerInstance.WorkflowById(DocumentKey, workflowAssociation.WorkflowId);
           
            List<WorkflowActivity> activities = new List<WorkflowActivity>();
            foreach (WorkflowActivity activity in workflow.Activities)
            {
                List<WorkflowParameter> workflowParam = workflow.GetActivityParameters(activity.Name);
                if (workflowParam.Count > 0)
                {
                    WorkflowActivity result = new WorkflowActivity();
                    result.Name = activity.Name;
                    result.Label = activity.Label;
                    result.Parameters = workflowParam;
                    activities.Add(result);
                }
            }

            this.ctrParameters.UpdateParameters(activities, isRerun ? workflowAssociation.RerunJob.Job :
                                                         workflowAssociation.Job);
        }

        /// <summary>
        /// Updates the rerun job.
        /// </summary>
        /// <param name="association">The association.</param>
        /// <returns></returns>
        private WorkflowJobStatus UpdateRerunJob(WorkflowAssociation association)
        {
            ITridentServer rerunServer = this.GetRerunServer(association);
            if (rerunServer == null)
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("InvalidRerunConnection"), MessageBoxIcon.Error);
                association.RerunJob = new RerunJob();
                this.isRerunPresent = false;
                this.rbtnOriginal.Checked = true;
                return WorkflowJobStatus.Failed;
            }

            association.RerunJob.Job = rerunServer.GetJobById(association.RerunJob.JobId);
            rerunServer.Dispose();
            return association.RerunJob.JobStatus;
        }

        /// <summary>
        /// Shows the rerun result.
        /// </summary>
        /// <param name="rerunJob">The rerun job.</param>
        private void ShowRerunResult(RerunJob rerunJob)
        {
            switch (rerunJob.JobStatus)
            {
                case WorkflowJobStatus.Processing:
                    ctrWorkflowOutput.IsBusy = true;
                    ctrDataProductOutput.IsBusy = true;
                    break;
                case WorkflowJobStatus.Terminated:
                case WorkflowJobStatus.Finished:
                    ctrWorkflowOutput.IsBusy = false;
                    ctrDataProductOutput.IsBusy = false;
                    this.SetResultControlState(true);
                    rerunJob.IsOutputPresent = rerunJob.Job.Outputs.Count > 0;
                    this.UpdateJobOuptut(rerunJob.Job.Outputs);
                    break;
            }
        }

        /// <summary>
        /// Updates the job ouptut.
        /// </summary>
        /// <param name="results">The results.</param>
        private void UpdateJobOuptut(List<ShallowJobOutput> results)
        {
            var workflowOutput = from output in results
                         where output.IsDataProduct != false
                         select output;

            var dataProducts = from output in results
                                 where output.IsDataProduct == false
                                 select output;

            ctrWorkflowOutput.UpdateResult(workflowOutput.ToList<ShallowJobOutput>());
            ctrDataProductOutput.UpdateResult(dataProducts.ToList<ShallowJobOutput>());
        }

        /// <summary>
        /// Shows the original job result.
        /// </summary>
        private void ShowOriginalJobResult(WorkflowJob job)
        {
            this.UpdateJobOuptut(job.Outputs);
            this.SetResultControlState(false);
        }

        /// <summary>
        /// Sets the state of the result control.
        /// </summary>
        /// <param name="isVisible">if set to <c>true</c> [is visible].</param>
        private void SetResultControlState(bool isVisible)
        {
            this.pnlOriginalToRerunSwitch.Visible = this.isRerunPresent;
        }

        /// <summary>
        /// Opens the output with default application.
        /// </summary>
        private void OpenOutputWithDefaultApplication(byte[] data, string fileName)
        {
            if (data != null)
            {
                // open with associated application
                string path = Path.Combine(DirectoryHelper.CreateTemporaryDirectory(), fileName);
                StreamHelper.WriteToFile(path, data);
                StartProcess(path);
            }

            data = null;
        }

        /// <summary>
        /// Starts the process.
        /// </summary>
        /// <param name="path">The path.</param>
        private void StartProcess(string path)
        {
            ProcessStartInfo info = new ProcessStartInfo(path);
            info.UseShellExecute = true;
            info.ErrorDialog = true;
            info.ErrorDialogParentHandle = this.Handle;

            Process process = new Process();
            process.StartInfo = info;
            process.Start();
            Trace.WriteLine("Started process for file " + path, TraceLevel.Info.ToString());
        }

        /// <summary>
        /// Gets the job result.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        private byte[] GetJobResultContent(ref string fileName)
        {
            byte[] resultData = null;
            WorkflowAssociation association = this.SelectedAssociation;
            if (association != null)
            {
                if (rbtnRerun.Checked)
                {
                    ITridentServer rerunServer = this.GetRerunServer(association);
                    if (rerunServer != null)
                    {
                        foreach (ShallowJobOutput result in association.RerunJob.Job.Outputs)
                        {
                            if (result.Name.Compare(fileName))
                            {
                                fileName += result.Extension;
                                resultData = rerunServer.GetJobOutputById(result.Id).Contents;
                                break;
                            }
                        }

                        rerunServer.Dispose();
                    }
                    else
                    {
                        MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("InvalidRerunConnection"), MessageBoxIcon.Error);
                        association.RerunJob.PromptForReconnect = true;
                    }
                }
                else
                {
                    foreach (ShallowJobOutput result in association.Job.Outputs)
                    {
                        if (result.Name.Compare(fileName))
                        {
                            fileName += result.Extension;
                            Model model = this.ControllerInstance.GetModelByDocumentKey(DocumentKey);
                            Guid fileId = model.Modules[result.Id].File.Guid;
                            resultData = this.ControllerInstance.DataStore.GetFileData(DocumentKey, fileId);
                            break;
                        }
                    }
                }
            }

            return resultData;
        }

        /// <summary>
        /// Gets the rerun server.
        /// </summary>
        /// <param name="association">The association.</param>
        /// <returns></returns>
        private ITridentServer GetRerunServer(WorkflowAssociation association)
        {
            ITridentServer rerunServer = null;
            if (association.RerunJob.PromptForReconnect)
            {
                rerunServer = ControllerInstance.ServerBroker.GetVerifiedServer(association.RerunJob.Connection);
                if (rerunServer == null)
                {
                    association.RerunJob.PromptForReconnect = false;
                }
            }

            return rerunServer;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the Click event of the btnEditWorkflowAssociation control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnEditWorkflowAssociation_Click(object sender, System.EventArgs e)
        {
            WorkflowAssociation association = this.SelectedAssociation;
            if (association != null)
            {
                if (ControllerInstance.CheckDocumentProtection(DocumentKey))
                {
                    return;
                }

                using (EditAssociation editAssociationForm = new EditAssociation())
                {
                    editAssociationForm.Initialize(DocumentKey, ControllerInstance, association);
                    if (DialogResult.OK == editAssociationForm.ShowDialog(this))
                    {
                        association.Name = editAssociationForm.AssociationName;
                        association.Annotation = editAssociationForm.Annotation;
                        ControllerInstance.AssociationChanged(DocumentKey, ControllerInstance.SelectedPicture);
                    }
                }
            }
            else
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
            }
            //Controller.ShowTaskPane(TaskPaneKey.EditWorkflow, true);
        }

        /// <summary>
        /// Saves the rerun-job outputs to file.
        /// </summary>
        /// <param name="jobOutputs">The job outputs.</param>
        /// <param name="job">The rerun job.</param>
        /// <returns></returns>
        private static IList<string> SaveJobOutputsToFile(List<JobOutput> jobOutputs, RerunJob job)
        {
            IList<string> filePaths = new List<string>();
            string directoryPath = DirectoryHelper.CreateTemporaryDirectory();

            foreach (JobOutput jobOutput in jobOutputs)
            {
                string filePath = Path.Combine(directoryPath, job.GetJobOutputFileName(jobOutput.Id));
                StreamHelper.WriteToFile(filePath, jobOutput.Contents);
                filePaths.Add(filePath);


            }

            return filePaths;
        }

        /// <summary>
        /// Saves the workflowjob outputs to file.
        /// </summary>
        /// <param name="jobOutputs">The job outputs.</param>
        /// <param name="job">The workflow job.</param>
        /// <returns></returns>
        private static IList<string> SaveJobOutputsToFile(List<JobOutput> jobOutputs, WorkflowJob job)
        {
            IList<string> filePaths = new List<string>();
            string directoryPath = DirectoryHelper.CreateTemporaryDirectory();

            foreach (JobOutput jobOutput in jobOutputs)
            {
                string filePath = Path.Combine(directoryPath, job.GetWorkflowOutputFileName(jobOutput.Id));
                StreamHelper.WriteToFile(filePath, jobOutput.Contents);
                filePaths.Add(filePath);


            }

            return filePaths;
        }

        /// <summary>
        /// Gets the required parameters.
        /// </summary>
        /// <param name="association">The association.</param>
        /// <param name="isInput">if set to <c>true</c> [is input].</param>
        /// <param name="isOrginal">if set to <c>true</c> [is orginal].</param>
        /// <returns></returns>
        private IList<AppendixParameter> GetRequiredParameters(WorkflowAssociation association, bool isInput, bool isOrginal)
        {
            IList<AppendixParameter> result = new List<AppendixParameter>();
            Workflow workflow = this.ControllerInstance.WorkflowById(DocumentKey, association.WorkflowId);
            WorkflowJob rerunJob = isOrginal ? association.Job : association.RerunJob.Job;
            string value = string.Empty;
            foreach (WorkflowParameter parameter in workflow.Parameters)
            {
                if (parameter.IsInput == isInput)
                {
                    JobParameter param = rerunJob.GetParameter(parameter.BelongsToActivity, parameter.Name);
                    value = ParameterHelper.GetValue(parameter, param);
                    result.Add(new AppendixParameter(parameter.BelongsToActivity + "." + parameter.Name, value));
                }
            }

            return result.Distinct();            
        }        

        /// <summary>
        /// Handles the SelectedIndexChanged event of the lvWorkflowAssociations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvWorkflowAssociations_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            if (this.doNotUpdateUI)
            {
                return;
            }

            WorkflowAssociation workflowAssociation = this.SelectedAssociation;
            this.doNotUpdateUI = true;
            this.isRerunPresent = workflowAssociation != null && workflowAssociation.RerunJob.JobId != Guid.Empty;
            if (this.isRerunPresent)
            {
                this.rbtnRerun.Checked = true;
            }
            else
            {
                this.rbtnOriginal.Checked = true;
            }

            this.doNotUpdateUI = false;
            this.UpdateWorkflowDetails(workflowAssociation, true);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the rbtnOriginal control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void rbtnOriginal_CheckedChanged(object sender, EventArgs e)
        {
            this.isOrginal = !rbtnRerun.Checked;
            this.ctrWorkflowOutput.IsRerunSelected = rbtnRerun.Checked;
            this.ctrDataProductOutput.IsRerunSelected = rbtnRerun.Checked;
            this.UpdateWorkflowDetails(this.SelectedAssociation, false);
        }

        /// <summary>
        /// Handles the Tick event of the RerunTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RerunTimer_Tick(object sender, System.EventArgs e)
        {
            if (this.handlingRerun)
            {
                return;
            }

            try
            {
                this.handlingRerun = true;
                this.rerunTimer.Enabled = false;
                WorkflowAssociation association = this.SelectedAssociation;
                if (association != null)
                {
                    if (association.RerunJob.JobId != Guid.Empty)
                    {
                        WorkflowJobStatus status = this.UpdateRerunJob(association);
                        if (this.rbtnRerun.Checked)
                        {
                            this.ShowRerunResult(association.RerunJob);
                        }

                        if (status == WorkflowJobStatus.Processing)
                        {
                            this.rerunTimer.Enabled = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, this);
            }
            finally
            {
                this.handlingRerun = false;
            }
        }

        /// <summary>
        /// Handles the OnPictureSelectionChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.PictureSelectionChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnPictureSelectionChanged(object sender, PictureSelectionChangedEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                this.workflowAssociationList = new List<WorkflowAssociation>();
                this.workflowAssociationList = this.ControllerInstance.GetAllWorkflowAssociations(DocumentKey, e.Picture);
                this.UpdateAssociatedWorkflows();
            }
        }

        /// <summary>
        /// Handles the OnAssociationChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.AssociationChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnAssociationChanged(object sender, AssociationChangedEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                this.workflowAssociationList = new List<WorkflowAssociation>();
                if (e.Picture != null)
                {
                    for (int i = 0; i < e.Associations.Count; i++)
                    {
                        if (e.Associations[i].Picture.Key.Compare(e.Picture.Key))
                        {
                            this.workflowAssociationList.Add(e.Associations[i]);
                        }
                    }

                    this.ControllerInstance.SelectPictureByDocument(this.DocumentKey, e.Picture.Key);
                }

                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new UIControllerDelegate(this.UpdateAssociatedWorkflows));
                }
                else
                {
                    this.UpdateAssociatedWorkflows();
                }
            }
        }

        /// <summary>
        /// Handles the OnRerunCompleted event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.RerunCompletedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnRerunCompleted(object sender, RerunCompletedEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                foreach (ListViewItem item in this.lvWorkflowAssociations.Items)
                {
                    WorkflowAssociation association = item.Tag as WorkflowAssociation;
                    if (association != null && association.LocalId == e.Association.LocalId)
                    {
                        this.doNotUpdateUI = true;
                        this.isRerunPresent = true;
                        item.Selected = true;
                        this.doNotUpdateUI = false;
                        association.RerunJob = e.Association.RerunJob;

                        if (this.rbtnRerun.Checked)
                        {
                            this.UpdateWorkflowDetails(association, true);
                        }
                        else
                        {
                            this.rbtnRerun.Checked = true;
                        }

                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the btnExport control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnExport_Click(object sender, EventArgs e)
        {
            ITridentServer tridentServer = null;
            try
            {
                WorkflowAssociation selectedAssociation = this.SelectedAssociation;
                if (selectedAssociation != null)
                {
                    bool userCancelled;
                    Workflow workflow = this.ControllerInstance.WorkflowById(DocumentKey, selectedAssociation.WorkflowId);
                    tridentServer = this.GetTridentServerForLinkedWorkflow(workflow, out userCancelled);
                    if (tridentServer != null || !workflow.Linked)
                    {
                        this.ExportWorkflow(workflow.Id, workflow.Name, tridentServer);
                    }
                    else if (!userCancelled)
                    {
                        MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoConnection"), MessageBoxIcon.Error);
                    }
                }
                else
                {
                    MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
                }
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, this);
            }
            finally
            {
                if (tridentServer != null)
                {
                    tridentServer.Dispose();
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the btnDeleteWorkflowAssociation control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDeleteWorkflowAssociation_Click(object sender, EventArgs e)
        {
            WorkflowAssociation selectedAssociation = this.SelectedAssociation;
            if (selectedAssociation != null)
            {
                ControllerInstance.DeleteWorkflowAssociation(DocumentKey, selectedAssociation.LocalId);
            }
            else
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
            }
        }

        /// <summary>
        /// Called when [save result clicked].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Presentation.SelectedFileEventArgs"/> instance containing the event data.</param>
        private void OnSaveResultClicked(object sender, SelectedFileEventArgs e)
        {
            string fileName = e.FileName;
            if (!fileName.IsNullOrEmpty())
            {
                byte[] data = this.GetJobResultContent(ref fileName);
                if (data == null)
                {
                    return;
                }

                using (SaveFileDialog saveDialog = new SaveFileDialog())
                {
                    saveDialog.FileName = fileName;
                    if (saveDialog.ShowDialog(this) == DialogResult.OK)
                    {
                        StreamHelper.WriteToFile(saveDialog.FileName, data);
                    }
                }

                data = null;
            }
            else
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoResultSelected"));
            }
        }

        /// <summary>
        /// Called when [result double clicked].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Presentation.SelectedFileEventArgs"/> instance containing the event data.</param>
        private void OnResultDoubleClicked(object sender, SelectedFileEventArgs e)
        {
            if (!e.FileName.IsNullOrEmpty())
            {
                string fileName = e.FileName;
                byte[] data = this.GetJobResultContent(ref fileName);
                this.OpenOutputWithDefaultApplication(data, fileName);
                data = null;
            }
        }

        /// <summary>
        /// Called when [insert result clicked].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Presentation.SelectedFileEventArgs"/> instance containing the event data.</param>
        private void OnInsertResultClicked(object sender, SelectedFileEventArgs e)
        {            
            if (!e.FileName.IsNullOrEmpty())
            {                
                string fileName = e.FileName;
                OutputType outputType = e.OutPutType;
                bool orginal = this.isOrginal;
                WorkflowAssociation association = this.SelectedAssociation;                
                using (InsertResultIntoDocumentForm form =
                       new InsertResultIntoDocumentForm(fileName, outputType, this.isOrginal, association))
                {
                    if (form.ShowDialog(this) == DialogResult.OK && !form.Failed)
                    {                        
                        bool isWorkflowOutput = (e.OutPutType == OutputType.Workflow) ? true : false;
                        AppendixResult appendixResult = new AppendixResult(association.Name, form.Comment, outputType, this.isOrginal);
                        if (!isOrginal)
                        {
                            appendixResult.ScheduledBy = association.RerunJob.Job.ScheduledBy;
                            appendixResult.ResultFilePaths = SaveJobOutputsToFile(form.Outputs, association.RerunJob);
                        }
                        else
                        {
                            appendixResult.ScheduledBy = association.Job.ScheduledBy;
                            appendixResult.ResultFilePaths = SaveJobOutputsToFile(form.Outputs, association.Job);                      
                        }

                        if (form.InsertParameters)
                        {
                            appendixResult.RequiredInputParameters = this.GetRequiredParameters(association, true, isOrginal);
                            appendixResult.RequiredOutputParameters = this.GetRequiredParameters(association, false, isOrginal);
                        }

                        if (form.SingleResult)
                        {
                            FileInfo fileInfo = new FileInfo(appendixResult.ResultFilePaths[0]);
                            appendixResult.IsImage = FileHelper.IsImageFile(fileInfo.Extension);
                        }

                        this.ControllerInstance.AddAppendixEntry(appendixResult);
                        if (!orginal)
                        {
                            association.RerunJob.ResultInserted = true;
                        }
                    }
                }
            }
            else
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoResultSelected"));
            }
        }

        /// <summary>
        /// Handles the Click event of the btnRerun control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRerun_Click(object sender, EventArgs e)
        {
            WorkflowAssociation selectedAssociation = this.SelectedAssociation;
            if (selectedAssociation != null)
            {
                RerunWorkflowCommand rerunCommand = new RerunWorkflowCommand(this.ControllerInstance);
                rerunCommand.Invoke(true);
                this.ControllerInstance.RerunClicked(this.DocumentKey, selectedAssociation);
            }
            else
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
            }
        }

        #endregion

        #region ITaskPaneControl Implementation

        /// <summary>
        /// Sets the dock position.
        /// </summary>
        /// <param name="dockPosition">The dock position.</param>
        public void SetDockPosition(TaskPaneDockPositions dockPosition)
        {
            if ((dockPosition & (TaskPaneDockPositions.Left | TaskPaneDockPositions.Right | TaskPaneDockPositions.Floating)) != 0)
            {
                this.SetVertical(true);
            }
            else
            {
                this.SetVertical(false);
            }
        }

        /// <summary>
        /// Sets the vertical.
        /// </summary>
        /// <param name="controlIsVertical">if set to <c>true</c> [control is vertical].</param>
        private void SetVertical(bool controlIsVertical)
        {
            this.SuspendLayout();

            // Reduce the splitter distances to avoid ending up with an invalid
            // value when changing the orientation of the split panel controls
            int third = 0;
            if (controlIsVertical)
            {
                this.splitContainer1.SplitterDistance = this.splitContainer1.MinimumSize.Height;
                this.splitContainer2.SplitterDistance = this.splitContainer2.MinimumSize.Height;
                third = this.Height / 3;
            }
            else
            {
                this.splitContainer1.SplitterDistance = this.splitContainer1.MinimumSize.Width;
                this.splitContainer2.SplitterDistance = this.splitContainer2.MinimumSize.Width;
                third = this.Width / 3;
            }

            // splitcontainter orientation is opposite of the control's orientation
            this.splitContainer1.Orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;
            this.splitContainer2.Orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;

            this.splitContainer1.SplitterDistance = third;
            this.splitContainer2.SplitterDistance = third;

            this.ResumeLayout();
        }

        #endregion

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the specified the privately held resources.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> disposes resources.</param>
        private void Disposing(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    ControllerInstance.OnPictureSelectionChanged -= new System.EventHandler<PictureSelectionChangedEventArgs>(Controller_OnPictureSelectionChanged);
                    ControllerInstance.OnAssociationChanged -= new System.EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
                    base.Disposing();
                }
            }

            this.disposed = true;
        }

        #endregion
    }
}
